Skip to content

feat(loading): add content-shaped skeletons for every route - #467

Merged
yhabib merged 7 commits into
mainfrom
ux/route-skeletons
Oct 6, 2026
Merged

yhabib merged 7 commits into
mainfrom
ux/route-skeletons

Conversation

@yhabib

@yhabib yhabib commented Oct 1, 2026 •

Copy link
Copy Markdown
Collaborator

Motivation

/voting, the proposal page, and the transaction list showed the same three grey bars whatever was behind them. /dashboard, /neurons, /accounts, and /settings had no pending state, so they showed a blank frame. Split from #447.

Changes

  • Added a skeletons/ set: one shell per route, plus shared list, card, and row shapes.
  • Wired the route shells as pendingComponent on all six routes.
  • Replaced the inline skeletons in the modals, the accounts list, and the transaction list with the shared rows.
  • Removed MultipleSkeletons.
  • Kept the /neurons header while the neurons query loads, and rendered the next-page proposals skeleton only during a fetch.

Prev. #466

yhabib added 2 commits October 1, 2026 08:48
Move the 120ms anti flicker delay into the `.skeleton` class, so every
skeleton holds its space at once and reveals itself late. Remove
`useDelayedFlag` and `LOADING_INDICATOR_DELAY`.

Mark skeleton bars `aria-hidden` and add `SkeletonScreen`, which
announces one "loading" for each region. Stop `animate-pulse` under
`prefers-reduced-motion`.

Fix the card skeletons that hid data already in hand or changed the
card height when they left.
Add a `skeletons/` set of content-shaped shells: one per route, plus
the list, card, and row shapes they share. Wire them as
`pendingComponent` on all six routes.

Replace the inline list skeletons in the modals and the transaction
list with the shared rows. Remove `MultipleSkeletons`.
@zeropath-ai

zeropath-ai Bot commented Oct 1, 2026 •

Copy link
Copy Markdown

✅ No security or compliance issues detected. Reviewed everything up to 4254859.

Security Overview
Detected Code Changes
Change Type Relevant files
Enhancement ► src/governance-app-frontend/src/common/components/skeletons/AccountsSkeleton.tsx
    Implement accounts skeleton layout
► src/governance-app-frontend/src/common/components/skeletons/DashboardSkeleton.tsx
    Implement dashboard skeleton layout
► src/governance-app-frontend/src/common/components/skeletons/NeuronsSkeleton.tsx
    Implement neurons skeleton layout
► src/governance-app-frontend/src/common/components/skeletons/ProposalDetailSkeleton.tsx
    Implement proposal detail skeleton
► src/governance-app-frontend/src/common/components/skeletons/ProposalListSkeleton.tsx
    Implement proposal list skeletons
► src/governance-app-frontend/src/common/components/skeletons/SettingsSkeleton.tsx
    Implement settings skeletons
► src/governance-app-frontend/src/common/components/skeletons/SkeletonAccountCard.tsx
    Skeleton account card component
► src/governance-app-frontend/src/common/components/skeletons/SkeletonFollowingCard.tsx
    Skeleton following card components
► src/governance-app-frontend/src/common/components/skeletons/SkeletonListRows.tsx
    Skeleton list row components
► src/governance-app-frontend/src/common/components/skeletons/SkeletonPageHeader.tsx
    Skeleton page header component
► src/governance-app-frontend/src/common/components/skeletons/SkeletonStatCard.tsx
    Skeleton stat card component
► src/governance-app-frontend/src/common/components/skeletons/SkeletonTransactionList.tsx
    Skeleton transaction list components
► src/governance-app-frontend/src/common/components/skeletons/VotingSkeleton.tsx
    Voting skeleton composition
► src/governance-app-frontend/src/common/components/skeletons/SkeletonTransactionList.tsx
    Skeleton transaction list components
► src/governance-app-frontend/src/common/components/skeletons/SkeletonScreen.tsx
    Skeleton screen wrapper
► src/governance-app-frontend/src/common/components/skeletons/SkeletonPickerRows.tsx
    Skeleton picker rows
► src/governance-app-frontend/src/common/components/skeletons/SkeletonAccountCard.tsx
    Skeleton account card component
► src/governance-app-frontend/src/common/components/skeletons/SkeletonProposalCards.tsx
    Skeleton proposal cards
► src/governance-app-frontend/src/common/components/skeletons/SkeletonPageHeader.tsx
    Skeleton page header component
► src/governance-app-frontend/src/common/components/skeletons/ProposalListSkeleton.tsx
    Skeleton proposal list skeletons
► src/governance-app-frontend/src/common/components/skeletons/NeuronsSkeleton.tsx
    Skeletons for Neurons route
► src/governance-app-frontend/src/common/components/skeletons/ProposalDetailSkeleton.tsx
    Proposal detail skeletons
► src/governance-app-frontend/src/common/components/skeletons/SettingsSkeleton.tsx
    Settings skeletons
► src/governance-app-frontend/src/common/components/skeletons/SkeletonListRows.tsx
    List rows skeletons
► src/governance-app-frontend/src/common/components/skeletons/ProposalDetailSkeleton.tsx
    Proposal detail skeletons
► src/governance-app-frontend/src/common/components/skeletons/ProposalListSkeleton.tsx
    Proposal list skeletons
► src/governance-app-frontend/src/common/components/skeletons/VotingSkeleton.tsx
    Voting skeleton layout
► src/governance-app-frontend/src/features/accounts/components/AccountsList.tsx
    Show skeletons when loading accounts
► src/governance-app-frontend/src/features/accounts/components/RecentTransactions.tsx
    Use SkeletonTransactionRows for loading state
► src/governance-app-frontend/src/features/addressBook/components/AddressBookModal.tsx
    Use SkeletonAddressBookRows for loading state
► src/governance-app-frontend/src/features/transactions/components/TransactionListDialog.tsx
    Use SkeletonTransactionList for loading state
► src/governance-app-frontend/src/features/voting/components/AdvancedFollowingModal.tsx
    Use SkeletonListRows for loading state
► src/governance-app-frontend/src/features/voting/components/FolloweePicker.tsx
    Use SkeletonPickerRows for loading state
► src/governance-app-frontend/src/features/voting/components/SimpleFollowingModal.tsx
    Use SkeletonPickerRows for loading state
► src/governance-app-frontend/src/features/voting/components/VotingOverviewAdvanced.tsx
    Replace with skeleton-based layout in Overview skeleton
► src/governance-app-frontend/src/routes/_auth/accounts/index.tsx
    Pending component now AccountsSkeleton
► src/governance-app-frontend/src/routes/_auth/dashboard/index.tsx
    Pending component now DashboardSkeleton
► src/governance-app-frontend/src/routes/_auth/neurons/index.tsx
    Pending component now NeuronsSkeleton
► src/governance-app-frontend/src/routes/_auth/settings/index.tsx
    Pending state skeletons integration (SettingsSkeleton)
Refactor ► src/governance-app-frontend/src/features/transactions/components/TransactionListDialog.tsx
    Replace MultipleSkeletons with SkeletonTransactionList
► src/governance-app-frontend/src/features/addressBook/components/AddressBookModal.tsx
    Switch loading to SkeletonAddressBookRows
► src/governance-app-frontend/src/features/voting/components/AdvancedFollowingModal.tsx
    Switch to SkeletonTopicRows
► src/governance-app-frontend/src/features/voting/components/FolloweePicker.tsx
    Switch to SkeletonPickerRows
► src/governance-app-frontend/src/features/voting/components/SimpleFollowingModal.tsx
    Switch to SkeletonPickerRows
► src/governance-app-frontend/src/routes/_auth/dashboard/index.tsx
    Introduce DashboardSkeleton as pending component

@github-actions

github-actions Bot commented Oct 1, 2026 •

Copy link
Copy Markdown

📊 Build Bundle Stats

The latest build generated the following assets:

dist/index.html                                           2.55 kB │ gzip:   0.87 kB
dist/assets/index-7koOkrvZ.css                          133.82 kB │ gzip:  21.89 kB
dist/assets/externalServices-CK4eoRia.js                  0.19 kB │ gzip:   0.16 kB
dist/assets/address-BBgU5ncI.js                           0.20 kB │ gzip:   0.15 kB
dist/assets/icpLedger-DkLaeIsO.js                         0.23 kB │ gzip:   0.21 kB
dist/assets/Skeleton-FN1pxWt8.js                          0.44 kB │ gzip:   0.32 kB
dist/assets/useTvlValue-AXZrIdFh.js                       0.57 kB │ gzip:   0.39 kB
dist/assets/numbers-DZLocVe6.js                           0.58 kB │ gzip:   0.36 kB
dist/assets/service-nrUectL1.js                           0.59 kB │ gzip:   0.38 kB
dist/assets/PageHeader-Czh9Jiub.js                        0.77 kB │ gzip:   0.45 kB
dist/assets/CertifiedBadge-NfowFlA9.js                    0.81 kB │ gzip:   0.48 kB
dist/assets/useIcpIndex-g4z4QWwL.js                       1.07 kB │ gzip:   0.63 kB
dist/assets/rolldown-runtime-CMxvf4Kt.js                  1.21 kB │ gzip:   0.67 kB
dist/assets/EmptyActionState-faPUFgJS.js                  1.61 kB │ gzip:   0.83 kB
dist/assets/addressBook-C_G_NNGf.js                       1.66 kB │ gzip:   0.99 kB
dist/assets/useGovernanceAppCanister-BKT1UHXf.js          1.91 kB │ gzip:   0.96 kB
dist/assets/AnimatedNumber-DplhApS0.js                    1.96 kB │ gzip:   1.12 kB
dist/assets/useIcpIndexTransactionsPolling-BSbQCojs.js    2.03 kB │ gzip:   1.10 kB
dist/assets/advancedFeatures-BWtkvsxh.js                  2.29 kB │ gzip:   1.08 kB
dist/assets/tickers-CpZiSknz.js                           3.10 kB │ gzip:   1.47 kB
dist/assets/Switch-CIGbZHmQ.js                            3.22 kB │ gzip:   1.44 kB
dist/assets/AlertDialog-BXofOb9y.js                       4.18 kB │ gzip:   1.25 kB
dist/assets/ToggleGroup-C6YqhwQW.js                       4.71 kB │ gzip:   1.76 kB
dist/assets/badge-GbWg_4Im.js                             6.17 kB │ gzip:   2.16 kB
dist/assets/SensitiveValue-OuGkOKJd.js                    7.33 kB │ gzip:   3.29 kB
dist/assets/routes-BQTpKRHp.js                            7.42 kB │ gzip:   2.45 kB
dist/assets/spamFilter-BbXMm40N.js                        8.57 kB │ gzip:   3.83 kB
dist/assets/MutationDialog-fyo5Xl5S.js                    9.55 kB │ gzip:   3.47 kB
dist/assets/events-Cyh0UJ5h.js                           10.38 kB │ gzip:   4.12 kB
dist/assets/_id-4KPgUgzw.js                              14.45 kB │ gzip:   4.35 kB
dist/assets/_auth-BIsS7-T7.js                            17.76 kB │ gzip:   6.54 kB
dist/assets/accounts-DEa0oyjJ.js                         23.31 kB │ gzip:   8.13 kB
dist/assets/settings-DVkN2kc-.js                         41.59 kB │ gzip:  11.96 kB
dist/assets/GetTokens-xjJrDLZC.js                        43.91 kB │ gzip:  15.96 kB
dist/assets/dashboard-CDUnTEaS.js                        50.21 kB │ gzip:  15.07 kB
dist/assets/button-x9VFnpAj.js                           59.64 kB │ gzip:  20.42 kB
dist/assets/voting-D0rHI6J9.js                           67.72 kB │ gzip:  21.07 kB
dist/assets/neurons-BEHxM376.js                          68.38 kB │ gzip:  20.36 kB
dist/assets/vendor-md-JDuLPwHg.js                        73.29 kB │ gzip:  19.98 kB
dist/assets/neuronDetail-CISi8KYK.js                     81.79 kB │ gzip:  24.11 kB
dist/assets/index-Dzuui6q4.js                           101.03 kB │ gzip:  28.90 kB
dist/assets/vendor-jsqr-CLmE8syL.js                     129.98 kB │ gzip:  47.50 kB
dist/assets/vendor-tanstack-DoHRSdRg.js                 161.18 kB │ gzip:  49.34 kB
dist/assets/vendor-core-react-qLCIanUQ.js               189.81 kB │ gzip:  59.76 kB
dist/assets/vendor-recharts-_Fr3yQ4W.js                 223.45 kB │ gzip:  64.00 kB
dist/assets/vendor-icp-2gDAGydl.js                      411.80 kB │ gzip: 104.29 kB
dist/assets/vendor-libs-BYkMuR5L.js                     554.93 kB │ gzip: 181.17 kB

Copilot AI previously approved these changes Oct 1, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟢 Approved

The loading shells are consistently integrated, accessible, and preserve the existing data and error flows.

Review effort: Balanced
Findings: None

What changed in this PR

Adds content-shaped loading skeletons across primary routes and related lists/modals.

Changes:

  • Adds reusable route, card, row, and list skeletons.
  • Wires skeletons into six route pending states and query loading states.
  • Removes the generic MultipleSkeletons component.
File Description
routes/​_auth/​voting/​proposals/​$id/​index.tsx Adds proposal-detail loading states.
routes/​_auth/​voting/​index.tsx Adds voting and proposal-list skeletons.
routes/​_auth/​settings/​index.tsx Adds the settings pending shell.
routes/​_auth/​neurons/​index.tsx Adds route and query skeletons.
routes/​_auth/​dashboard/​index.tsx Adds the dashboard pending shell.
routes/​_auth/​accounts/​index.tsx Adds the accounts pending shell.
features/​voting/​components/​VotingOverviewSimple.tsx Improves the simple overview placeholder.
features/​voting/​components/​VotingOverviewAdvanced.tsx Improves the advanced overview placeholder.
features/​voting/​components/​SimpleFollowingModal.tsx Reuses picker-row skeletons.
features/​voting/​components/​FolloweePicker.tsx Reuses picker-row skeletons.
features/​voting/​components/​AdvancedFollowingModal.tsx Reuses topic-row skeletons.
features/​transactions/​components/​TransactionListDialog.tsx Adds transaction-shaped placeholders.
features/​addressBook/​components/​AddressBookModal.tsx Adds address-book row placeholders.
features/​accounts/​components/​RecentTransactions.tsx Reuses compact transaction rows.
features/​accounts/​components/​AccountsList.tsx Adds account-card placeholders.
common/​components/​skeletons/​VotingSkeleton.tsx Defines the voting route shell.
common/​components/​skeletons/​SkeletonTransactionList.tsx Defines transaction placeholders.
common/​components/​skeletons/​SkeletonStatCard.tsx Defines reusable statistic cards.
common/​components/​skeletons/​SkeletonPageHeader.tsx Defines reusable page headers.
common/​components/​skeletons/​SkeletonListRows.tsx Defines reusable modal/list rows.
common/​components/​skeletons/​SkeletonAccountCard.tsx Defines account-card placeholders.
common/​components/​skeletons/​SettingsSkeleton.tsx Defines the settings route shell.
common/​components/​skeletons/​ProposalListSkeleton.tsx Defines proposal-list placeholders.
common/​components/​skeletons/​ProposalDetailSkeleton.tsx Defines proposal-detail placeholders.
common/​components/​skeletons/​NeuronsSkeleton.tsx Defines neuron route placeholders.
common/​components/​skeletons/​DashboardSkeleton.tsx Defines the dashboard route shell.
common/​components/​skeletons/​AccountsSkeleton.tsx Defines the accounts route shell.
common/​components/​MultipleSkeletons.tsx Removes the generic bar skeleton.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Base automatically changed from ux/skeleton-reveal-delay to main October 6, 2026 08:09
…e-skeletons

# Conflicts:
#	src/governance-app-frontend/src/app/styles/main.css
#	src/governance-app-frontend/src/common/components/QueryStates.tsx
#	src/governance-app-frontend/src/common/components/skeletons/SkeletonScreen.tsx
#	src/governance-app-frontend/src/features/dashboard/components/TotalStakedCard.tsx
#	src/governance-app-frontend/src/features/voting/components/AdvancedFollowingModal.tsx
@yhabib
yhabib requested a balanced review from Copilot October 6, 2026 08:42
Copilot AI dismissed their stale review, a newer Copilot review was requested October 6, 2026 08:42

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Unresolved loading-state regressions affect layout stability, mobile rendering, and screen-reader announcements.

Review effort: Balanced
Findings: 5 Medium severity

Open (5)

Comment thread src/governance-app-frontend/src/common/components/skeletons/SettingsSkeleton.tsx Outdated
Comment thread src/governance-app-frontend/src/common/components/skeletons/SkeletonStatCard.tsx Outdated
Comment thread src/governance-app-frontend/src/common/components/skeletons/VotingSkeleton.tsx Outdated
Comment thread src/governance-app-frontend/src/routes/_auth/neurons/index.tsx
Comment thread src/governance-app-frontend/src/routes/_auth/voting/index.tsx Outdated
…e pages

Mirror all six settings sections and the version footer. Show the
proposal placeholders only when showProposals is on. Keep the neurons
header while the query loads. Clamp the stat card bars to the card
width. Render the next-page skeleton only during a fetch.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🔵 Needs a closer look

Several placeholders still differ structurally from the content they replace, causing avoidable layout shifts.

Review effort: Balanced
Findings: None

Resolved since last review (5)
Previously missed (3)

In code that hasn't changed since last review

Medium severity Match NeuronCard metadata row structure

src/​governance-app-frontend/​src/​common/​components/​skeletons/​NeuronsSkeleton.tsx:27

NeuronCard.tsx:148–239 always renders six metadata rows, but this placeholder reserves only one two-column row and a button. Even without optional warnings or actions, each loaded card is substantially taller. Since this also serves as the query-loading fallback, a non-empty response pushes subsequent cards down on mobile. Match the amount header and six padded label/value rows.

Medium severity Match proposal summary and voting card order

src/​governance-app-frontend/​src/​common/​components/​skeletons/​ProposalDetailSkeleton.tsx:41

The proposal page puts the Markdown summary in the first card, followed by ProposalDetailsVoting in the second (routes/_auth/voting/proposals/$id/index.tsx:186–190). This shell reverses those regions, so the text and voting placeholders do not occupy the space their content replaces. Put SkeletonText below the first card's header and move the voting placeholders into the second card.

Medium severity Match responsive transfer transaction layout

src/​governance-app-frontend/​src/​common/​components/​skeletons/​SkeletonTransactionList.tsx:29

AccountTransactionItem uses three detail lines for ordinary transfers, with the amount below them on phones and in a separate column from sm upward (TransactionItem.tsx:102–186). This shell instead packs everything into two text rows with desktop padding at every width. Transfer results therefore make the list substantially taller on mobile and can resize the dialog. Use the same responsive grid and padding, with separate detail lines and an amount cell.

Mirror the six NeuronCard rows and its APY badge. Put the summary text
in the first proposal card and the vote bar and totals in the second.
Use the responsive transaction grid, with the amount below the details
on a phone.
@yhabib

yhabib commented Oct 6, 2026

Copy link
Copy Markdown
Collaborator Author

Also matched the neuron card, the proposal page, and the transaction row.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Confirmed skeleton layout mismatches undermine the intended content-shaped loading transitions.

Review effort: Balanced
Findings: 3 Medium severity

Open (3)

Comment thread src/governance-app-frontend/src/common/components/skeletons/NeuronsSkeleton.tsx Outdated

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🔵 Needs a closer look

The voting pending shell ignores advanced-following mode, causing a layout jump between loading states.

Review effort: Balanced
Findings: None

Resolved since last review (3)
Previously missed (1)

In code that hasn't changed since last review

Medium severity Pending shell ignores advanced feature flag and shifts layout

src/​governance-app-frontend/​src/​common/​components/​skeletons/​VotingSkeleton.tsx:21

With AdvancedFeature.AdvancedFollowing enabled, the page's query-loading state uses a four-row card, but this pending shell always renders the compact simple card. If the queries are still loading when the route mounts, the proposal toggle and list move down before data arrives. Pass the same feature flag from VotingPending and render the matching overview shape here, preferably sharing it with VotingOverviewAdvanced.

Share the simple and advanced following card skeletons between the
overviews and the route shell, and pick the card from the same feature
flag as the page.
@yhabib

yhabib commented Oct 6, 2026

Copy link
Copy Markdown
Collaborator Author

The voting shell now follows the advanced following flag too.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🔵 Needs a closer look

Mobile placeholder overflow and insufficient dashboard card height remain unresolved.

Review effort: Balanced
Findings: None

Previously missed (2)

In code that hasn't changed since last review

Medium severity Match loading account card layout to enabled subaccounts

src/​governance-app-frontend/​src/​common/​components/​skeletons/​DashboardSkeleton.tsx:38

Below md, the dashboard cards stack, so this single action placeholder leaves the account card too short. AccountCard.tsx:91-100 renders two stacked actions; with subaccounts enabled, AccountsCard.tsx:73-103 shows preview rows plus an action, including two rows while loading. The missing content pushes subsequent cards down when the page replaces the pending shell. Use the same features.subaccounts flag as the dashboard to render the corresponding rows/actions and spacing.

Medium severity Constrain loading name bar to available column width

src/​governance-app-frontend/​src/​common/​components/​skeletons/​SkeletonListRows.tsx:18

At a 320px viewport, the address-book drawer and row padding leave about 130px for this column. The fixed 160px name bar overflows it and overlaps the first action placeholder. Add max-w-full so the loading row stays within its available width, as the real entry's truncated name does.

Mirror AccountCard and AccountsCard in the dashboard shell, picked from
the same feature flag as the page. Clamp the address book name bar to
its column.
@yhabib

yhabib commented Oct 6, 2026

Copy link
Copy Markdown
Collaborator Author

Also handled the account card and the address book row.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟢 Approved

The presentation-focused changes have no supported blocking defects, and the previously reported issues are addressed.

Review effort: Balanced
Findings: None

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't really read this, but Claude is saying this is ok.

@yhabib
yhabib merged commit 72c95de into main Oct 6, 2026
14 checks passed
@yhabib
yhabib deleted the ux/route-skeletons branch October 6, 2026 13:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants